﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using Wheels.Properties;

namespace Wheels
{
    [Obsolete("Don't use.", true)]
    static class RuntimeHacking
    {
#pragma warning disable 1591

        public enum StackCrawlMark
        {
            //LookForMe = 0,
            LookForMyCaller = 1,
            //LookForMyCallersCaller = 2,
            //LookForThread = 3,
        }
        delegate MethodBase GetStackMethodDelegate(ref StackCrawlMark mark);
        private static readonly GetStackMethodDelegate smGetCurrentMethodDelegate;

        static RuntimeHacking()
        {
            #region GetCallerMethod

            // based on the solution from http://stackoverflow.com/questions/5143068/call-private-method-retaining-call-stack

            var m = new DynamicMethod("GetCallerMethod", typeof(MethodBase), new Type[] { typeof(StackCrawlMark).MakeByRefType() }, true);
            var gen = m.GetILGenerator();
            gen.Emit(OpCodes.Ldarg_0); //NO type checking here!
            gen.Emit(OpCodes.Call, Type.GetType("System.Reflection.RuntimeMethodInfo", true).GetMethod("InternalGetCurrentMethod", BindingFlags.Static | BindingFlags.NonPublic));
            gen.Emit(OpCodes.Ret);

            smGetCurrentMethodDelegate = (GetStackMethodDelegate)m.CreateDelegate(typeof(GetStackMethodDelegate));

            #endregion
        }

        public static MethodBase GetCallerMethod(ref StackCrawlMark mark)
        {
            if (mark != StackCrawlMark.LookForMyCaller)
                throw new ArgumentOutOfRangeException("mark", Resources.WantLookForMyCaller);

            return smGetCurrentMethodDelegate(ref mark);
        }
    }
}
